#ifndef __ASTAR_DATA__HIKARI__
#define __ASTAR_DATA__HIKARI__
#include <vector>
#include "point.h"
using namespace std;
using namespace stdext;

class Hashable{
public:
	virtual long long hashcode()=0;
};



class FPoint:Hashable{
private:
	float x;
	float y;
public:
	FPoint(){
		x=0.0;
		y=0.0;
	}

	FPoint(float x, float y){
		this->x=x;
		this->y=y;
	}

	float getX(){
		return x;
	}

	float getY(){
		return y;
	}

	void setX(float x){
		this->x=x;
	}

	void setY(float y){
		this->y=y;
	}

	void setXY(float x, float y){
		this->x=x;
		this->y=y;
	}

	virtual long long hashcode(){
		unsigned long long hash=7;
		hash=83*hash+(long)(Utils::doubleHash(x)^((unsigned long long)Utils::doubleHash(x)>>32));
		hash=83*hash+(long)(Utils::doubleHash(y)^((unsigned long long)Utils::doubleHash(y)>>32));
		return hash;
	}
};


class ConnectionPoint{
private:
	Point point1;
	Point point2;
public:

	void setPoint1(int x, int y){
		point1.setXY(x,y);
	}

	void setPoint2(int x, int y){
		point2.setXY(x,y);
	}

	Point&getPoint1(){
		return point1;
	}

	Point&getPoint2(){
		return point2;
	}
};


class Cell{
private:
	int activity;
public:
	Cell(){
		activity=0;
	}

	bool isWalkable(){
		return activity==0;
	}

	void setActive(bool active){
		if(active==true){
			this->activity=0;
		}else{
			this->activity=1;
		}
	}

	void addObstacle(){
		activity++;
	}

	void removeObstacle(){
		activity--;
		if(activity<0){
			activity=0;
		}
	}
};

class NavmeshRegion;

class RegionConnection{
private:
	
	NavmeshRegion*region;

	vector<ConnectionPoint*>connectionPoints;
public:
	~RegionConnection(){
		removeConnectionPoints();
	}

	void removeConnectionPoints();

	void addConnectionPoint(ConnectionPoint*point){
		connectionPoints.push_back(point);
	}

	vector<ConnectionPoint*>&getConnectionPoints(){
		return connectionPoints;
	}

	void setConnectedRegion(NavmeshRegion*region){
		this->region=region;
	}

	NavmeshRegion*getRegion(){
		return region;
	}

	void readConnectionPoints(FileSection*section);
};


class NavmeshRegion:Hashable{
private:
	unsigned int xCount;
	unsigned int yCount;
	double width;
	double height;
	double x;
	double y;
	Cell**cells;
	unsigned int id;
	hash_map<NavmeshRegion*,RegionConnection*>connectionMap;
public:
	NavmeshRegion(){
		cells=NULL;
		xCount=0;
		yCount=0;
		width=0;
		height=0;
		x=0;
		y=0;
		id=0;
	}

	void setId(unsigned int id){
		this->id=id;
	}

	unsigned int getId(){
		return id;
	}

	long long hashcode(){
		unsigned long long hash=7;
		hash=83*hash+(long)(Utils::doubleHash(x)^((unsigned long long)Utils::doubleHash(x)>>32));
		hash=83*hash+(long)(Utils::doubleHash(y)^((unsigned long long)Utils::doubleHash(y)>>32));
		hash=83*hash+(long)(Utils::doubleHash(width)^((unsigned long long)Utils::doubleHash(width)>>32));
		hash=83*hash+(long)(Utils::doubleHash(height)^((unsigned long long)Utils::doubleHash(height)>>32));
		hash=83*hash+xCount;
		hash=83*hash+yCount;
		return hash;
	}

	hash_map<NavmeshRegion*,RegionConnection*>&getConnections(){
		return connectionMap;
	}

	NavmeshRegion(unsigned int xCount,unsigned int yCount,double width,double height,double x,double y){
		cells=NULL;
		this->xCount=xCount;
		this->yCount=yCount;
		this->x=x;
		this->y=y;
		this->width=width;
		this->height=height;
	}

	~NavmeshRegion(){
		removeCells();
	}

	void createCells();
	void removeCells();
	void clearConnections();
	Cell* getCell(unsigned int x, unsigned int y);
	void setCell(unsigned int x, unsigned int y, Cell*cell);
	void addConnection(NavmeshRegion*region, RegionConnection*connection);
	bool checkVisionLine(int x1, int y1, int x2, int y2);
	bool isActiveCellFast(int x, int y);
	void setxCount(unsigned int count){
		this->xCount=count;
	}

	unsigned int getxCount(){
		return xCount;
	}

	void setyCount(unsigned int count){
		this->yCount=count;
	}

	unsigned int getyCount(){
		return yCount;
	}

	void setWidth(double width){
		this->width=width;
	}

	double getWidth(){
		return width;
	}

	void setHeight(double height){
		this->height=height;
	}

	double getHeight(){
		return height;
	}

	void setX(double x){
		this->x=x;
	}

	double getX(){
		return x;
	}

	void setY(double y){
		this->y=y;
	}

	double getY(){
		return y;
	}

	bool pointInNavmesh(float x, float y){
		if(x>=this->x && x<(this->x+this->width) && y>=this->y && y<(this->y+this->height)){
			return true;
		}

		return false;
	}

	bool getIndexOfCell(float x, float y, int&xIndex, int&yIndex){
		if(!pointInNavmesh(x,y)){
			xIndex=-1;
			yIndex=-1;
			return false;
		}

		float cellSize=(float)width/(float)xCount;
		xIndex=(int)((x-this->x)/cellSize);
		yIndex=(int)((y-this->y)/cellSize);
		return true;
	}

	bool getPositionOfCell(int xIndex, int yIndex,float&xpos, float&ypos){
		if(xIndex<0||yIndex<0||(unsigned int)xIndex>=xCount || (unsigned int)yIndex>=yCount){
			return false;
		}
		
		float w=(float)(width/(float)xCount);
		float h=(float)(height/(float)yCount);
		xpos = (float)(x + xIndex * w + w / 2);
		ypos = (float)(y + yIndex * h + h / 2);
		return true;
	}

	virtual float GoalDistanceEstimate( NavmeshRegion*nodeGoal ); // Heuristic function which computes the estimated cost to the goal node
	virtual bool IsGoal( NavmeshRegion*nodeGoal ); // Returns true if this node is the goal node
	virtual bool GetSuccessors( astarPointer::AStarSearch<NavmeshRegion*> * astarsearch, NavmeshRegion **parent_node ); // Retrieves all successors to this node and adds them via astarsearch.addSuccessor()
	virtual float GetCost( NavmeshRegion*successor ); // Computes the cost of travelling from this node to the successor node
	virtual bool IsSameState( NavmeshRegion*rhs ); // Returns true if this node is the same as the rhs node
};

#endif